package com.bosicloud.cmb.ibp.aws.adapter.service.impl;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ecs.AmazonECS;
import com.amazonaws.services.ecs.AmazonECSClientBuilder;
import com.amazonaws.services.ecs.model.*;
import com.amazonaws.services.ecs.model.LoadBalancer;
import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancing;
import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancingClientBuilder;
import com.amazonaws.services.elasticloadbalancingv2.model.*;
import com.bosicloud.cmb.ibp.aws.adapter.commom.dto.DeployeAppParamDTO;
import com.bosicloud.cmb.ibp.aws.adapter.commom.dto.DeployeAppResponseDTO;
import com.bosicloud.cmb.ibp.aws.adapter.commom.dto.GetServiceInfoDTO;
import com.bosicloud.cmb.ibp.aws.adapter.service.DeployEcsService;
import com.bosicloud.cmb.ibp.aws.adapter.utils.AWSUtils;
import com.bosicloud.cmb.ibp.commom.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author Jack Peng / ziliang.peng@bosicloud.com
 */
@Slf4j
@Service
public class DeployEcsServiceImpl implements DeployEcsService {


    @Override
    public DeployeAppResponseDTO deployeApps(DeployeAppParamDTO params) {

        log.info("DeployApps: {}", params);

        Regions region = AWSUtils.checkRegion(params.getRegion().trim().toLowerCase());

        AWSStaticCredentialsProvider credentialsProvider = AWSUtils.credentialsProvider(params.getAccessKey(), params.getSecretKey());

        AmazonECS amazonECS = AmazonECSClientBuilder.standard()
                .withCredentials(credentialsProvider)
                .withRegion(region)
                .build();

        AmazonElasticLoadBalancing amazonELB = AmazonElasticLoadBalancingClientBuilder.standard()
                .withCredentials(credentialsProvider)
                .withRegion(region)
                .build();

        DescribeLoadBalancersResult describeLoadBalancersResult = amazonELB.describeLoadBalancers(new DescribeLoadBalancersRequest()
                .withLoadBalancerArns(params.getElbArn()));
        Optional<List<com.amazonaws.services.elasticloadbalancingv2.model.LoadBalancer>> loadBalancers =
                Optional.ofNullable(describeLoadBalancersResult.getLoadBalancers());

        StringBuilder elbUrl = new StringBuilder("http://");

        if (loadBalancers.isPresent() && loadBalancers.get().size() == 1) {

            com.amazonaws.services.elasticloadbalancingv2.model.LoadBalancer lb = loadBalancers.get().get(0);

            log.debug(lb.toString());

            elbUrl.append(lb.getDNSName());
            elbUrl.append(":");
            elbUrl.append(params.getElbPort());
            elbUrl.append(params.getDeployPath());
        } else {
            throw new BusinessException("ELB ARN未发现.");
        }

        Map<String, String> logOpts = new HashMap<>();
        logOpts.put("awslogs-group", params.getLogGroup());
        logOpts.put("awslogs-region", region.getName());
        logOpts.put("awslogs-stream-prefix", params.getTaskName());

        ContainerDefinition containerDefinition = new ContainerDefinition()
                .withName(params.getTaskName())
                .withImage(params.getImage())
                .withMemory(params.getMemory())
                .withCpu(params.getCpu())
                .withLogConfiguration(new LogConfiguration()
                        .withLogDriver(LogDriver.Awslogs)
                        .withOptions(logOpts))
                .withPortMappings(new PortMapping()
                        .withContainerPort(params.getContainerPort()));

        if (params.getEnvironment() != null && params.getEnvironment().size() > 0) {
            containerDefinition.setEnvironment(params.getEnvironment());
        }

        RegisterTaskDefinitionRequest registerTaskDefinitionRequest = new RegisterTaskDefinitionRequest()
                .withFamily(params.getTaskName())
                .withMemory(params.getMemory().toString())
                .withCpu(params.getCpu().toString())
                .withContainerDefinitions(containerDefinition);

        if (params.getTaskRoleArn() != null && "".equals(params.getTaskRoleArn().trim())) {
            registerTaskDefinitionRequest.setTaskRoleArn(params.getTaskRoleArn());
        }

        amazonECS.registerTaskDefinition(registerTaskDefinitionRequest);

        Optional<List<TargetGroup>> tgs;
        String tgName = "TG-" + params.getTaskName();
        try {
            tgs = Optional.ofNullable(amazonELB.createTargetGroup(new CreateTargetGroupRequest()
                    .withTargetType(TargetTypeEnum.Instance)
                    .withName(tgName)
                    .withHealthCheckPath(params.getCheckHealthPath())
                    .withHealthCheckTimeoutSeconds(params.getHealthCheckTimeout())
                    .withHealthCheckIntervalSeconds(params.getHealthCheckTimeout() + 10)
                    .withPort(params.getContainerPort())
                    .withProtocol(ProtocolEnum.HTTP)
                    .withVpcId(params.getVpcId()))
                    .getTargetGroups());
        } catch (DuplicateTargetGroupNameException e) {
            tgs = Optional.ofNullable(amazonELB
                    .describeTargetGroups(new DescribeTargetGroupsRequest()
                            .withNames(tgName)).getTargetGroups());
        }


        if (tgs.isPresent() && tgs.get().size() == 1) {

            String targetGroupArn = tgs.get().get(0).getTargetGroupArn();

            Optional<Listener> listener = Optional.empty();
            try {
                Optional<List<Listener>> createListenerResult = Optional.ofNullable(amazonELB.createListener(new CreateListenerRequest()
                        .withLoadBalancerArn(params.getElbArn())
                        .withDefaultActions(new Action()
                                .withType(ActionTypeEnum.Forward)
                                .withTargetGroupArn(targetGroupArn))
                        .withPort(params.getElbPort()).withProtocol(ProtocolEnum.HTTP)).getListeners());

                if(createListenerResult.isPresent() && createListenerResult.get().size() == 1) {
                    listener = Optional.ofNullable(createListenerResult.get().get(0));
                }

            } catch (DuplicateListenerException e) {

                log.info("LB端口已经被使用, 路径被添加到现有监听器.");

                listener = amazonELB.describeListeners(new DescribeListenersRequest()
                        .withPageSize(400)
                        .withLoadBalancerArn(params.getElbArn())).getListeners().stream()
                        .filter(rs -> Objects.nonNull(rs))
                        .filter(rs -> Objects.equals(rs.getPort(), params.getElbPort()))
                        .findAny();

            }

            // Bind Rules
            if(listener.isPresent()) {

                List<Rule> rules = amazonELB.describeRules(new DescribeRulesRequest()
                        .withListenerArn(listener.get().getListenerArn())
                        .withPageSize(400)).getRules();

                int priority = rules.size() + 2;

                amazonELB.createRule(new CreateRuleRequest()
                        .withPriority(priority)
                        .withConditions(new RuleCondition()
                                .withField("path-pattern")
                                .withValues(params.getDeployPath().trim()))
                        .withListenerArn(listener.get().getListenerArn())
                        .withActions(new Action()
                                .withType(ActionTypeEnum.Forward)
                                .withTargetGroupArn(targetGroupArn)));
            } else {
                throw new BusinessException("绑定规则到LB失败：未发现监听器.");
            }

            CreateServiceResult createServiceResult = amazonECS.createService(new CreateServiceRequest()
                    .withCluster(params.getEcsClusterName())
                    .withServiceName(params.getTaskName())
                    .withTaskDefinition(params.getTaskName())
                    .withDesiredCount(params.getInstanceNum())
                    .withLaunchType(LaunchType.EC2)
                    .withRole(params.getEcsRoleArn())
                    .withHealthCheckGracePeriodSeconds(60 * 3)
                    .withLoadBalancers(new LoadBalancer()
                            .withTargetGroupArn(targetGroupArn)
                            .withContainerName(params.getTaskName())
                            .withContainerPort(params.getContainerPort())));

            return DeployeAppResponseDTO
                    .builder()
                    .elbUrl(elbUrl.toString())
                    .serviceArn(createServiceResult.getService().getServiceArn()).build();

        } else {
            throw new BusinessException("未获取到创建的TargetGroup.");
        }

    }

    @Override
    public List<com.amazonaws.services.ecs.model.Service> serviceInfo(GetServiceInfoDTO params) {

        Regions region = AWSUtils.checkRegion(params.getRegion().trim().toLowerCase());

        AWSStaticCredentialsProvider credentialsProvider = AWSUtils.credentialsProvider(params.getAccessKey(), params.getSecretKey());

        AmazonECS amazonECS = AmazonECSClientBuilder.standard()
                .withCredentials(credentialsProvider)
                .withRegion(region)
                .build();

        return amazonECS.describeServices(new DescribeServicesRequest()
                .withCluster(params.getClusterName())
                .withServices(params.getServiceNames()))
                .getServices();
    }
}
